package db

import (
	"jtdevice/api/device/model"
	"jtdevice/pkg/orm"
	"time"

	"github.com/panjf2000/ants/v2"
)

type handler struct {
	status   chan *model.Status
	devalarm chan *model.AlarmDet
	alarm    chan *model.Alarm
	online   chan *model.Online
	isActive bool
}

func (o *handler) PublishStatus(a *model.Status) {
	o.status <- a
}

func (o *handler) PublishAlarm(a *model.Alarm) {
	if a.Flag == 1 {
		o.devalarm <- &a.AlarmDet
	} else {
		o.alarm <- a
	}
}

type dispatchmodel interface {
	model.Alarm | model.Status | model.AlarmDet
}

func dispatch[T dispatchmodel](ch chan *T, pools int) {
	var dataArr []T
	ticker := time.NewTicker(time.Second * 2)
	p, _ := ants.NewPoolWithFunc(pools, func(v interface{}) {
		data := v.([]T)
		orm.DbCreate(data)
	}) // 协程池
	invokeDbData := func() {
		data := make([]T, len(dataArr))
		copy(data, dataArr)
		if err := p.Invoke(data); err == nil {
			dataArr = dataArr[:0]
		}
	}
	defer p.Release()
	defer close(ch)
	for {
		select {
		case v := <-ch:
			if v == nil {
				orm.DbCreate(dataArr)
				return
			}
			dataArr = append(dataArr, *v)
			if len(dataArr) > 30 {
				invokeDbData()
			}
		case <-ticker.C:
			if len(dataArr) > 0 {
				invokeDbData()
			}
		}
	}
}

func (o *handler) PublishOnline(a *model.Online) {
	o.online <- a
}

// dispatchOnline 批量处理数据
func (o *handler) dispatchOnline() {
	p, _ := ants.NewPoolWithFunc(5, func(v interface{}) {
		data := v.(model.Online)
		if data.OnlineTime != "" {
			orm.DbCreate(&data)
		} else {
			orm.DbModel(&data).Select("OfflineTime", "OfflineStatus", "UpTraffic", "DownTraffic").Where("GUID = ?", data.GUID).Updates(&data)
		}
	}) // 协程池
	defer p.Release()
	defer close(o.online)
	for v := range o.online {
		if v == nil {
			break
		}
		p.Invoke(*v)
	}
}

var (
	dispatcher = &handler{
		isActive: false,
		status:   make(chan *model.Status, 20),
		devalarm: make(chan *model.AlarmDet, 1),
		alarm:    make(chan *model.Alarm, 1),
		online:   make(chan *model.Online, 1),
	}
)

func Dispatcher() *handler {
	return dispatcher
}

func (o *handler) Run() {
	o.isActive = true
	go dispatch[model.Status](o.status, 50)
	go dispatch[model.AlarmDet](o.devalarm, 5)
	go dispatch[model.Alarm](o.alarm, 5)
	go o.dispatchOnline()
}

func (o *handler) Stop() {
	o.devalarm <- nil
	o.alarm <- nil
	o.status <- nil
	o.online <- nil
}

func (o *handler) IsRunning() bool {
	return o.isActive
}
